package com.zf.springlearn.lifecycle;

import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.*;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.Objects;

/**
 * @author zhangfei
 * @date 2024/4/7 17:35
 **/
@Component
@Data
public class OneBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware
        , EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware
        , ApplicationContextAware, InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(OneBean.class);
    public static final String beanName = "oneBean";
    private String desc;
    public static int order = 0;
    public static int valueIndex = 0;
    private String step = "where....";
    @Autowired
    private AutowiredBean autowiredBean;

    @Resource
    private ResourceBean resourceBean;

    @Value("${spring.application.name}")
    private String appName;

    public OneBean() {
        LOGGER.info(order + "--Constructor::instantiating...");
        order++;
    }

    @PostConstruct
    public void init() {
        LOGGER.info(order + "--@PostConstruct");
        order++;
    }

    @Override
    public void setBeanName(String name) {
        LOGGER.info(order + "--BeanNameAware::setBeanName");
        order++;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        LOGGER.info(order + "--BeanClassLoaderAware::setBeanClassLoader");
        order++;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        LOGGER.info(order + "--BeanFactoryAware::setBeanFactory");
        order++;
    }

    @Override
    public void setEnvironment(Environment environment) {
        LOGGER.info(order + "--EnvironmentAware::setEnvironment");
        order++;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        LOGGER.info(order + "--EmbeddedValueResolverAware::setEmbeddedValueResolver");
        order++;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        LOGGER.info(order + "--ResourceLoaderAware::setResourceLoader");
        order++;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        LOGGER.info(order + "--ApplicationEventPublisherAware::setApplicationEventPublisher");
        order++;
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        LOGGER.info(order + "--MessageSourceAware::setMessageSource");
        order++;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        LOGGER.info(order + "--ApplicationContextAware::setApplicationContext");
        order++;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info(order + "--InitializingBean::afterPropertiesSet");
        order++;
    }

    @PreDestroy
    public void destroy() {
        LOGGER.info(order + "--@PreDestroy");
        order++;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OneBean oneBean = (OneBean) o;
        return desc.equals(oneBean.desc) && step.equals(oneBean.step) && resourceBean.equals(oneBean.resourceBean) && autowiredBean.equals(oneBean.autowiredBean) && appName.equals(oneBean.appName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(desc, step, resourceBean, autowiredBean, appName);
    }

    @Component
    public static class TestProcessor implements InstantiationAwareBeanPostProcessor {
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                LOGGER.info(order + "--InstantiationAwareBeanPostProcessor::postProcessBeforeInstantiation");
                order++;
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
        }

        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                LOGGER.info(order + "--InstantiationAwareBeanPostProcessor::postProcessAfterInstantiation");
                order++;
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
        }

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                LOGGER.info(order + "--BeanPostProcessor::postProcessBeforeInitialization");
                order++;
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                LOGGER.info(order + "--BeanPostProcessor::postProcessAfterInitialization");
                order++;
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }

        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                LOGGER.info(order + "--InstantiationAwareBeanPostProcessor::populating...");
                order++;
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
        }
    }

    @Component
    public static class DestoryProcessor implements DestructionAwareBeanPostProcessor {
        @Override
        public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                LOGGER.info(order + "--DestructionAwareBeanPostProcessor::destory...");
                order++;
            }
        }
    }

    @Component
    public static class DefinitionProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            if (registry.containsBeanDefinition(OneBean.beanName)) {
                LOGGER.info(order + "--BeanFactoryPostProcessor::postProcessBeanFactory");
                order++;
                BeanDefinition beanDefinition = registry.getBeanDefinition(OneBean.beanName);
                beanDefinition.getPropertyValues().addPropertyValue("desc", "hello...");
            }
        }
    }

    @Component
    public static class DebugProcessor implements InstantiationAwareBeanPostProcessor {

        private int lastHashCode = 0;

        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
        }

        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                if (bean.hashCode() != lastHashCode) {
                    LOGGER.info("value change--" + valueIndex + ":" + JSON.toJSONString(bean));
                    valueIndex++;
                    lastHashCode = bean.hashCode();
                }
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
        }

        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                if (bean.hashCode() != lastHashCode) {
                    LOGGER.info("value change--" + valueIndex + ":" + JSON.toJSONString(bean));
                    valueIndex++;
                    lastHashCode = bean.hashCode();
                }
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
        }

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                if (bean.hashCode() != lastHashCode) {
                    LOGGER.info("value change--" + valueIndex + ":" + JSON.toJSONString(bean));
                    valueIndex++;
                    lastHashCode = bean.hashCode();
                }
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals(OneBean.beanName)) {
                if (bean.hashCode() != lastHashCode) {
                    LOGGER.info("value change--" + valueIndex + ":" + JSON.toJSONString(bean));
                    valueIndex++;
                    lastHashCode = bean.hashCode();
                }
            }
            return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
}
